package com.lgywin.userserver.comm;


import com.lgywin.userserver.protobuf.LoginProto;
import com.lgywin.userserver.protobuf.MsgProto;
import com.lgywin.userserver.protobuf.NetProto;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import lombok.extern.slf4j.Slf4j;
import org.joou.UShort;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;

@Slf4j
@ChannelHandler.Sharable
/**
 * 必须和 LengthFieldBasedFrameDecoder 一起使用，确保接到的 ByteBuf 消息是完整的
 */
public class EntityCodec extends MessageToMessageCodec<ByteBuf, NetProto> {
    @Override
    public void encode(ChannelHandlerContext ctx, NetProto netProto, List<Object> outList) throws Exception {
        int protoCode = netProto.getProtoCode();
        if(protoCode == 5){
            MsgProto bean = netProto.getCls(0).unpack(MsgProto.class);
            log.info("EntityCodec 消息结果:" + bean.getCode());
        }
        if(protoCode == 4){
            LoginProto bean = netProto.getCls(0).unpack(LoginProto.class);
            log.info("EntityCodec 登录结果:" + bean.getUsername() + "=" + bean.getPassword());
        }

        // 实体的字节流
        byte[] bytes = netProto.toByteArray();

        ByteBuf out = ctx.alloc().buffer();
        // 数据包的长度 = 实体字节流的长度
        short dataPackageLength = (short) bytes.length;

        // 加密数据包
        bytes = SecurityUtil.Xor(bytes);

        // 写入长度
        byte[] lengthBytes = new byte[2];
        lengthBytes[0] = int0(dataPackageLength);
        lengthBytes[1] = int1(dataPackageLength);
        out.writeShort(ByteBuffer.wrap(lengthBytes).order(ByteOrder.BIG_ENDIAN).getShort());

        // 写入CRC码
        short crc = Crc.CalculateCrc16(bytes);
        byte[] crcBytes = new byte[2];
        crcBytes[0] = int0(crc);
        crcBytes[1] = int1(crc);
        out.writeShort(ByteBuffer.wrap(crcBytes).order(ByteOrder.BIG_ENDIAN).getShort());

        //log.info("写给客户端的整合包的长度=" + bytes.length + " CRC码=" + Crc.CalculateCrc16(bytes));

        // 写入数据包
        out.writeBytes(bytes);
        outList.add(out);
    }

    private static byte int3(int x) {
        return (byte) (x >> 24);
    }

    private static byte int2(int x) {
        return (byte) (x >> 16);
    }

    private static byte int1(int x) {
        return (byte) (x >> 8);
    }

    private static byte int0(int x) {
        return (byte) (x >> 0);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 把小端数据转换成int
        //int i = ByteBuffer.wrap(fourBytes).order(ByteOrder.LITTLE_ENDIAN).getInt();

        // 把大端数据转换成int
        //int j = ByteBuffer.wrap(fourBytes).order(ByteOrder.BIG_ENDIAN).getInt();

        // 读入数据包长度
        byte[] lengthBytes = new byte[2];
        lengthBytes[0] = in.readByte();
        lengthBytes[1] = in.readByte();
        short length = ByteBuffer.wrap(lengthBytes).order(ByteOrder.LITTLE_ENDIAN).getShort();
        // 读入crc码
        byte[] crcBytes = new byte[2];
        crcBytes[0] = in.readByte();
        crcBytes[1] = in.readByte();
        int crc = Integer.parseInt(UShort.valueOf(ByteBuffer.wrap(crcBytes).order(ByteOrder.LITTLE_ENDIAN).getShort()).toString());

        // 数据包的长度 = 实体字节流的长度
        byte[] bytes = new byte[length];
        // 读入 数据包
        in.readBytes(bytes);

        // CRC检验字节流
        int newCrc = Integer.parseInt(UShort.valueOf(Crc.CalculateCrc16(bytes)).toString());
        if (newCrc != crc) {
            InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
            log.error("CRC检验出错 " + address.getAddress() + address.getPort());
            return;
        }

        // 解密数据包
        bytes = SecurityUtil.Xor(bytes);

        // 读出实体类
        NetProto netProto = NetProto.parseFrom(bytes);
        out.add(netProto);
    }

}
